﻿using NightskyMimic.Domain;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace NightskyMimic.Util
{
    public static class Combinations
    {

        public static IEnumerable<IEnumerable<T>> SubsetsOf<T>(IList<T> set)
        {
            List<List<T>> results = new List<List<T>>();

            foreach (var result in GetSubsets(set))
                results.Add(result.ToList());

            foreach (List<T> rs in results)
            {
                rs.RemoveAll(x => x == null);
            }

            return results;
        }

        public static IEnumerable<IEnumerable<T>> GetSubsets<T>(IList<T> set)
        {
            var state = new BitArray(set.Count);
            do
                yield return Enumerable.Range(0, state.Count)
                                       .Select(i => state[i] ? set[i] : default(T));
            while (Increment(state));
        }

        static bool Increment(BitArray flags)
        {
            int x = flags.Count - 1;
            while (x >= 0 && flags[x]) flags[x--] = false;
            if (x >= 0) flags[x] = true;
            return x >= 0;
        }

        public static List<List<Attack>> AllAttacks(List<Creature> creatures)
        {
            List<List<Attack>> result = new List<List<Attack>>();

            List<List<Creature>> allSubsets = Combinations.SubsetsOf(creatures).Select(x => x as List<Creature>).ToList();
            foreach (List<Creature> set in allSubsets)
            {
                List<Attack> a = new List<Attack>();
                foreach (Creature c in set)
                {
                    a.Add(new Attack(c));
                }
                result.Add(a);
            }
            return result;
        }


        public static List<List<Block>> AllBlocks(List<Creature> blockers, List<Attack> attackers) 
        {
            var result = Execute(blockers, attackers, new List<Block>());
            
            //remove repetidos
            //for (int i = 0; i < result.Count; i++)
            //{
            //    var value = result[i];
            //    var auxList = result.Where(x => x != value).ToList();
            //    for (int j = 0; j < result.Count - 1; j++)
            //    {
            //        var other = auxList[j];
            //        if (isEqual(value, other))
            //        {
            //            result.RemoveAt(j);
            //        }
            //    }
            //}
            return result;
        }

        private static List<List<Block>> Execute(IEnumerable<Creature> defenders, IEnumerable<Attack> attackers, List<Block> bind)
        {
            var result = new List<List<Block>>();

            result.Add(bind);
            bind = new List<Block>(bind);

            foreach (var defender in defenders)
            {
                foreach (var attacker in attackers)
                {
                    bind.Add(new Block(defender, attacker.Target));

                    var subResult = Execute(defenders.Where(x => x != defender), attackers, bind);

                    //tira os repetidos
                    foreach (var sub in subResult) 
                    {
                        bool isUnique = true;
                        foreach (var r in result) 
                        {
                            if (isEqual(sub, r))
                            {
                                isUnique = false;
                                break;
                            }
                        }
                        if (isUnique)
                            result.Add(sub);
                    }
                    
                    //result.AddRange(subResult);

                    bind = new List<Block>();
                }

            }

            return result;
        }

        static bool isEqual(List<Block> l1, List<Block> l2)
        {
            if (l1.Count != l2.Count)
                return false;

            foreach (Block b in l1)
            {
                if (!l2.Contains(b))
                    return false;
            }

            foreach (Block b in l2)
            {
                if (!l1.Contains(b))
                    return false;
            }

            return true;
        }

    }
}
